﻿/*
* Activity Tracking Log v0.1.1 - http://activitytrackinglog.codeplex.com
* Activity Tracking Log for ASP.Net & ASP.Net MVC
* Copyright (c) 2011 Vadim Burac
* Licensed under the Microsoft Public License (Ms-PL)
* http://activitytrackinglog.codeplex.com/license
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using ActivityTrackingLog.Data;
using ActivityTrackingLog.Storage;
using ActivityTrackingLog.Utils;
using ActivityTrackingLog.Configuration;
using ActivityTrackingLog.Web.Utils;

namespace ActivityTrackingLog.Web.Helpers.ViewEngines
{
    internal class BaseRenderer<T> : IViewEngine
    {
        #region data

        protected ILogRepository _logRepository = null;
        private ColumnData[] _columns = null;
        private static readonly string _reportFullUrlTemplate = "{0}?entity={1}&view={2}&report={3}&page_size={4}&page_index={5}&cols={6}&asc_cols={7}&desc_cols={8}";
        private static readonly string _reportDataTemplate = "report_base_url: '{0}', 'entity': '{1}', 'view': '{2}', 'report': '{3}', 'page_size': '{4}', 'page_index': '{5}', 'cols': '{6}', 'asc_cols': '{7}', 'desc_cols': '{8}'";
        private static readonly string _pageReloadFunction = "javascript:return window.reloadReportPage('{0}', {1})";

        #endregion

        #region ctor

        public BaseRenderer(HtmlHelper.ViewTypeEnum viewType, HtmlHelper.ReportTypeEnum reportType)
        {
            _logRepository = Factory.LogRepository;
            ViewType = viewType;
            ReportType = reportType;
        }

        #endregion

        #region public props

        //public HtmlHelper.EntityEnum EntityType { get; set; }
        public string ReportBaseUrl { get; set; }
        public HtmlHelper.ViewTypeEnum ViewType { get; set; }
        public HtmlHelper.ReportTypeEnum ReportType { get; set; }
        public bool EmbedThemeStyles { get; set; }
        public int PageIndex { get; set; }
        public int PageSize { get; set; }
        public string UserId { get; set; }
        public ColumnData[] Columns 
        {
            get { return _columns; }
            set 
            { 
                _columns = value;
                SetDefaultSorting(_columns);
            }
        }

        public string ColumnsCsv
        {
            get
            {
                if (null == Columns || Columns.Length == 0)
                    return string.Empty;
                return Columns.Select(c => c.Name).Aggregate((current, next) => current + "," + next);
            }
        }

        /*public string AscColumnsCsv(bool invert = false)
        {
            var dir = invert ? Enums.SortDirectionEnum.Descending : Enums.SortDirectionEnum.Ascending;
            return GetSortedColumnsCsv(dir);
        }

        public string DescColumnsCsv(bool invert = false)
        {
            var dir = invert ? Enums.SortDirectionEnum.Ascending : Enums.SortDirectionEnum.Descending;
            return GetSortedColumnsCsv(dir);
        }*/

        public virtual string RenderContent()
        {
            int totalRecords = 0;
            string navigationControls = string.Empty;
            return GetContentTemplate()
                .Replace("{styles_placeholder}", GetStylesBody())
                .Replace("{content_placeholder}", GetContentBody(out totalRecords, out navigationControls))
                .Replace("{application_name}", Config.Log.ApplicationKey)
                .Replace("{assembly_version}", Build.GetVersion())
                .Replace("{total_records}", totalRecords.ToString())
                .Replace("{navigation_controls}", navigationControls)
                .Replace("{page_size}", this.PageSize.ToString())
                .Replace("{page_full_url}", this.GetPageFullUrl(this.PageIndex, true))
                .Replace("{page_data}", this.GetPageData(this.PageIndex, true))
                // config values
                .Replace("{log_config}", Config.Log.ToString())
                .Replace("{storage_config}", Config.Storage.ToString())
                .Replace("{client_access_config}", Config.ClientSideAccess.ToString())
                .Replace("{ui_config}", Config.Ui.ToString())
                .Replace("{api_config}", Config.Api.ToString());
        }

        #endregion

        #region virtual methods

        public virtual string GetContentTemplate()
        {
            throw new NotImplementedException();
        }

        public virtual string GetStylesBody()
        {
            return string.Empty;
        }

        public virtual string GetContentBody(out int totalRecords, out string navigationControls)
        {
            throw new NotImplementedException();
        }

        protected virtual KeyValuePair<string, Enums.SortDirectionEnum> GetDefaultColumnSort()
        {
            return new KeyValuePair<string,Enums.SortDirectionEnum>(null, Enums.SortDirectionEnum.None);
        }

        #endregion

        #region helpers

        private void SetDefaultSorting(ColumnData[] cols)
        {
            if (null != cols)
            {
                // check default sorting
                int sortedCols = cols.Where(c => c.SortDirection != Enums.SortDirectionEnum.None).Count();
                var defaultColumnSort = GetDefaultColumnSort();
                if (sortedCols == 0 && !string.IsNullOrWhiteSpace(defaultColumnSort.Key) && defaultColumnSort.Value != Enums.SortDirectionEnum.None)
                {
                    // add default sorting by COUNT DESC
                    var cntCol = cols.Where(c => c.Name.CompareIgnoreCase(defaultColumnSort.Key)).FirstOrDefault();
                    if (null != cntCol)
                        cntCol.SortDirection = defaultColumnSort.Value;
                }
            }
        }

        protected string GetPageFullUrl(int pageIndex = 0, bool stubPageSize = true, string[] ascCols = null, string[] descCols = null)
        {
            return FormatReportString(_reportFullUrlTemplate, pageIndex, stubPageSize, ascCols, descCols);
        }

        protected string GetPageData(int pageIndex = 0, bool stubPageSize = true, string[] ascCols = null, string[] descCols = null)
        {
            return "{" + FormatReportString(_reportDataTemplate, pageIndex, stubPageSize, ascCols, descCols) + "}";
        }

        protected string FlatListToJson(string[] attrNames, string[][] flatList)
        {
            // build body
            StringBuilder jsonBuilder = new StringBuilder();
            jsonBuilder.Append("[");

            for (int i = 0; i < flatList.Count(); i++)
            {
                var itemData = flatList[i];
                if (i != 0) jsonBuilder.Append(",");
                jsonBuilder.Append("{");
                for (int propIdx = 0; propIdx < attrNames.Length; propIdx++)
                {
                    var propName = attrNames[propIdx];
                    var value = itemData.Length > propIdx ? itemData[propIdx] : string.Empty;
                    if (propIdx != 0) jsonBuilder.Append(",");
                    jsonBuilder.Append(string.Format("\"{0}\":\"{1}\"", propName, value));
                }

                jsonBuilder.Append("}");
            }

            jsonBuilder.Append("]");
            return jsonBuilder.ToString();
        }

        private string FormatReportString(string template, int pageIndex, bool stubPageSize, string[] ascCols, string[] descCols)
        {
            return string.Format(template,
                    this.ReportBaseUrl,
                    typeof(T).GetTypeName(),
                    ViewType,
                    ReportType,
                    (!stubPageSize ? PageSize.ToString() : "page_size_stub"),
                    pageIndex,
                    ColumnsCsv,
                    null == ascCols ? GetSortedColumnsCsv(this.Columns.Where(c => c.SortDirection == Enums.SortDirectionEnum.Ascending).Select(c => c.Name).ToArray()) : GetSortedColumnsCsv(ascCols),
                    null == descCols ? GetSortedColumnsCsv(this.Columns.Where(c => c.SortDirection == Enums.SortDirectionEnum.Descending).Select(c => c.Name).ToArray()) : GetSortedColumnsCsv(descCols)
                );
        }

        protected string BuildTableHeader()
        {
            string thead = string.Empty;
            new List<ColumnData>(this.Columns).ForEach(colData =>
            {
                thead += GetColumnHeaderLayout(colData);
            });
            return thead;
        }

        protected string BuildDataTableBody(string[][] plainList)
        {
            bool alt = false;
            string[] tableRows = plainList.Select(item =>
            {
                string row = string.Format("<tr class=\"{0}\">", alt ? "alt" : "");
                alt = !alt;

                item.ToList().ForEach(value => row += "<td>" + value + "</td>");
                row += "<td class=\"last_column\"></td>";
                row += "</tr>";

                return row;
            }).ToArray();

            string tbody = tableRows.Length > 0 ? tableRows.Aggregate((c, n) => c + n) : "";

            return tbody;
        }

        private string GetSortedColumnsCsv(string[] colNames)
        {
            if (null == colNames || colNames.Length == 0)
                return string.Empty;

            return colNames.Aggregate((current, next) => current + "," + next);
        }

        protected string GetColumnHeaderLayout(ColumnData colData)
        {
            // columns sorting
            var ascCols = colData.SortDirection == Enums.SortDirectionEnum.None || colData.SortDirection == Enums.SortDirectionEnum.Descending ?
                new string[] {colData.Name} : new string[] {};
            var descCols = colData.SortDirection == Enums.SortDirectionEnum.Ascending ?
                new string[] { colData.Name } : new string[] { };
            // page reload function
            var pageReloadFunction = string.Format(_pageReloadFunction,
                GetPageFullUrl(this.PageIndex, false, ascCols, descCols),
                GetPageData(this.PageIndex, false, ascCols, descCols));
            string sortSuffix = colData.SortDirection == Enums.SortDirectionEnum.Ascending ? "(&uarr;)" :
                colData.SortDirection == Enums.SortDirectionEnum.Descending ? "(&darr;)" : "";

            return string.Format("<th class=\"sortable\" onclick=\"{0}\">{1}{2}</th>",
                pageReloadFunction,
                colData.Name,
                sortSuffix);
        }

        #endregion

        #region pager

        protected string GetNavigationControlsLayout(int totalRecords, int pageSize, int currentPageIndex)
        {
            if (pageSize <= 0)
                return "";

            int allowedPageLinks = 15;
            int totalPages = Convert.ToInt32(Math.Ceiling((float)totalRecords / pageSize));
            int currentPageNr = currentPageIndex + 1;

            string links = string.Empty;
            if (totalPages <= allowedPageLinks)
            {
                // build all available pages as links
                links = BuildPageLinks(1, totalPages, currentPageNr);
            }
            else
            {
                // build 5 first 5 middle and 5 last links
                links = BuildPageLinks(1, 5, currentPageNr);

                if (currentPageNr > 5 || currentPageNr < totalPages - 5 + 1)
                {
                    bool show1stGap = false;
                    bool show2ndGap = false;
                    int dFrom = currentPageNr - 5 - 1;
                    int dTo = (totalPages - 5) - currentPageNr;
                    if (dFrom > 2)
                    {
                        dFrom = 2;
                        show1stGap = true;
                    }
                    if (dTo > 2)
                    {
                        dTo = 2;
                        show2ndGap = true;
                    }
                    // build string
                    if (show1stGap)
                        links += "<span slass='navigation_hole'>...<span/>";
                    links += BuildPageLinks(currentPageNr - dFrom, currentPageNr + dTo, currentPageNr);
                    if (show2ndGap)
                        links += "<span slass='navigation_hole'>...<span/>";
                }
                else
                {
                    links += "<span slass='navigation_hole'>...<span/>";
                }

                links += BuildPageLinks((totalPages - 5) + 1, totalPages, currentPageNr);
            }

            int prevPage = currentPageNr <= 1 ? 1 : currentPageNr - 1;
            int nextPage = currentPageNr >= totalPages ? totalPages : currentPageNr + 1;

            return FormatLink(prevPage, "&lt;") + links + FormatLink(nextPage, "&gt;");
        }

        private string BuildPageLinks(int startPage, int endPage, int currentPageNr)
        {
            string links = string.Empty;
            for (int pageNr = startPage; pageNr <= endPage; pageNr++)
            {
                links += FormatLink(pageNr, pageNr.ToString(), pageNr == currentPageNr);
            }

            return links;
        }

        private string FormatLink(int pageIdx, string caption, bool current = false)
        {
            string linkTemplate = "<a class=\"navigation_link {3}\" href=\"#\" onclick=\"" + _pageReloadFunction + "\">{2}</a>";

            return string.Format(linkTemplate,
                GetPageFullUrl(pageIdx - 1),
                GetPageData(pageIdx - 1),
                caption,
                current ? "current" : "");
        }

        #endregion
    }
}
